package com.monkey.core.plugin.web.validator;

import com.monkey.core.exception.MonkeyException;

import javax.validation.constraints.Pattern;
import java.math.BigDecimal;
import java.net.IDN;
import java.util.Date;
import java.util.regex.Matcher;

import static java.util.regex.Pattern.CASE_INSENSITIVE;

/**
 * 数据校验工具类
 * Project  : monkey
 * Author   : Wu Tian Qiang
 * Date     : 2016/10/9
 */
public abstract class ValidationUtils {
    private static String ATOM = "[a-z0-9!#$%&'*+/=?^_`{|}~-]";
    private static String DOMAIN = ATOM + "+(\\." + ATOM + "+)*";
    private static String IP_DOMAIN = "\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\]";
    private final static java.util.regex.Pattern localPattern = java.util.regex.Pattern.compile(
            ATOM + "+(\\." + ATOM + "+)*", CASE_INSENSITIVE
    );

    private final static java.util.regex.Pattern domainPattern = java.util.regex.Pattern.compile(
            DOMAIN + "|" + IP_DOMAIN, CASE_INSENSITIVE
    );

    public static void assertFalse(Boolean expression, String message) {
        if (expression != null && expression) {
            throw new MonkeyException(message);
        }
    }

    public static void assertTrue(Boolean expression, String message) {
        if (expression != null && !expression) {
            throw new MonkeyException(message);
        }
    }


    /**
     * @param maxValue
     * @param validatedValue
     * @param inclusive      是否包含临界值
     * @param message
     */
    public static void decimalMax(BigDecimal maxValue, CharSequence validatedValue, boolean inclusive, String message) {
        if (validatedValue == null) {
            return;
        }
        int comparisonResult = new BigDecimal(validatedValue.toString()).compareTo(maxValue);
        if (inclusive) {
            if (comparisonResult > 0) {
                throw new MonkeyException(message);
            }
        } else {
            if (comparisonResult >= 0) {
                throw new MonkeyException(message);
            }
        }
    }

    public static void decimalMin(BigDecimal minValue, CharSequence validatedValue, boolean inclusive, String message) {
        if (validatedValue == null) {
            return;
        }
        int comparisonResult = new BigDecimal(validatedValue.toString()).compareTo(minValue);
        if (inclusive) {
            if (comparisonResult < 0) {
                throw new MonkeyException(message);
            }
        } else {
            if (comparisonResult <= 0) {
                throw new MonkeyException(message);
            }
        }
    }

    public static void digits(int maxIntegerLength, int maxFractionLength, CharSequence validatedValue, String message) {
        if (validatedValue == null) {
            return;
        }
        if (maxIntegerLength < 0 || maxIntegerLength < 0) {
            throw new MonkeyException(message);
        }

        BigDecimal bigNum = new BigDecimal(validatedValue.toString());

        int integerPartLength = bigNum.precision() - bigNum.scale();
        int fractionPartLength = bigNum.scale() < 0 ? 0 : bigNum.scale();

        if (maxIntegerLength < integerPartLength || maxFractionLength < fractionPartLength) {
            throw new MonkeyException(message);
        }
    }

    public static void future(Date validatedValue, String message) {
        if (validatedValue == null) {
            return;
        }
        if (validatedValue.before(new Date())) {
            throw new MonkeyException(message);
        }
    }

    public static void past(Date validatedValue, String message) {
        if (validatedValue == null) {
            return;
        }
        if (validatedValue.after(new Date())) {
            throw new MonkeyException(message);
        }
    }

    public static void max(BigDecimal maxValue, CharSequence validatedValue, String message) {
        if (validatedValue == null) {
            return;
        }
        if (new BigDecimal(validatedValue.toString()).compareTo(maxValue) > 0) {
            throw new MonkeyException(message);
        }
    }

    public static void min(BigDecimal minValue, CharSequence validatedValue, String message) {
        if (validatedValue == null) {
            return;
        }
        if (new BigDecimal(validatedValue.toString()).compareTo(minValue) < 0) {
            throw new MonkeyException(message);
        }
    }

    public static void notNull(Object validatedValue, String message) {
        if (validatedValue == null) {
            throw new MonkeyException(message);
        }
    }

    public static void null_(Object validatedValue, String message) {
        if (validatedValue != null) {
            throw new MonkeyException(message);
        }
    }

    public static void pattern(String regexp, Pattern.Flag[] flags, CharSequence validatedValue, String message) {
        if (validatedValue == null) {
            return;
        }
        int intFlag = 0;
        for (Pattern.Flag flag : flags) {
            intFlag = intFlag | flag.getValue();
        }
        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(regexp, intFlag);
        Matcher m = pattern.matcher(validatedValue);
        if (!m.matches()) {
            throw new MonkeyException(message);
        }

    }

    public static void length(int min, int max, CharSequence validatedValue, String message) {
        if (validatedValue == null) {
            return;
        }
        int length = validatedValue.length();
        if (length < min || length > max) {
            throw new MonkeyException(message);
        }
    }

    public static void notBlank(CharSequence validatedValue, String message) {
        if (validatedValue == null) {
            return;
        }
        if (validatedValue.toString().trim().length() <= 0) {
            throw new MonkeyException(message);
        }
    }

    public static void range(long min, long max, CharSequence validatedValue, String message) {
        min(new BigDecimal(min), validatedValue, message);
        max(new BigDecimal(max), validatedValue, message);
    }


    public static void email(CharSequence validatedValue, String message) {
        if (validatedValue == null || validatedValue.length() == 0) {
            return;
        }

        String[] emailParts = validatedValue.toString().split("@", 3);
        if (emailParts.length != 2) {
            throw new MonkeyException(message);
        }

        if (emailParts[0].endsWith(".") || emailParts[1].endsWith(".")) {
            throw new MonkeyException(message);
        }

        if (!matchPart(emailParts[0], localPattern)) {
            throw new MonkeyException(message);
        }

        if (!matchPart(emailParts[1], domainPattern)) {
            throw new MonkeyException(message);
        }
    }

    private static boolean matchPart(String part, java.util.regex.Pattern pattern) {
        try {
            part = IDN.toASCII(part);
        } catch (IllegalArgumentException e) {
            // occurs when the label is too long (>63, even though it should probably be 64 - see http://www.rfc-editor.org/errata_search.php?rfc=3696,
            // practically that should not be a problem)
            return false;
        }
        Matcher matcher = pattern.matcher(part);
        return matcher.matches();
    }

}
